import directoryParser
import getopt
import sys
import os
import nmapDatabase
import logging

'''
Created on 15 Mar 2010

@author: A Hopkins
'''

LOGGING_LEVELS= {'critical': logging.CRITICAL, 'error': logging.ERROR, 'warning':logging.WARNING, 'info':logging.INFO, 'debug':logging.DEBUG}

def usage():
    print ("=======================================================================================")
    print ("nmapResultParser v0.2 - A Hopkins - 08 August 2011")
    print ("=======================================================================================")
    print ("NmapResultParser takes the XML output from NMAP and temporarily stores the results into")
    print ("a database that can then be queried. The results can then be output to a file") 
    print ("which can then be used as a target list for other tools.")
    print ("")
    print ("Written in Python 3")
    print ("options:")
    print ("    -h                                This usage information")
    print ("    -i <directory>                    Specifies the directory to traverse for nmap files")
    print ("    -o <DatabaseName>                 Specify the database location to store results or query")
    print ("    -a                                Add results to an existing database specified with the -o")
    print ("                                      options. Default is to create attempt to create DB")
    print ("    -s <string>                       Filter which nmap xml files can parsed, by specifying a string")
    print ("                                      that the filename must start with")
    print ("    -q <query>                        The query that you would like to make against the database") 
    print ("")
    print ("There are three tables that can be queried - hosts, services and scriptResults")
    print ("")
    print ("=========================    =========================    =================================") 
    print ("| hosts                 |    | services              |    | scriptResults                 |")
    print ("=========================    =========================    =================================")
    print ("| ipaddress varchar(15) |    | ipaddress varchar(15) |    | ipaddress varchar(15)         |")
    print ("| macaddress char(17)   |    | protocol varchar(3)   |    | protocol varchar(3)           |")
    print ("| osName varchar(60)    |    | port varchar(5)       |    | port varchar(5)               |")
    print ("=========================    | service varchar(30)   |    | nmapscriptid varchar(10)      |")
    print ("                             | product varchar(30)   |    | scriptoutput varchar(1000)    |")
    print ("                             | state varchar(30)     |    =================================")
    print ("                             | extrainfo varchar(30) |")
    print ("                             =========================")
    print ("")
    #print ("To make a query you simply have to use the -q (--query) option together with the sql query")
    #print ("string you wish to make, for example:")
    print ("")
   # print ("python nmapResultParser.py -i nmap.xml -q \"select * from services where product = 'CherryPy httpd'\"")
   # print ("")
   # print ("python nmapResultParser.py -i \"E:\\test\\nmap\\fullTCP.xml\" -q \"select a.ipaddress,b.scriptOutput from services a,scriptResults b where a.serviceid = b.serviceid and b.nmapscriptId = 'robots.txt'\" -d")
    print ("")
    
def main():

    inputDirectory=""
    outputDB=""
    filenameStartsWith=""
    query=""
    loggingLevel="info"
    logName=""
    querySpecified = False
    inputSpecified = False
    dbOutputSpecified = False
    createDatabase = True
    createEmptyDB = False
    
    try:
        opts, args = getopt.getopt(sys.argv[1:], "ehi:o:as:q:O:l:", ["createEmptyDB", "help", "inputDirectory=", "outputDB=", "filenameStarts=", "query=", "logOutputFilename=", "loggingLevel="])
    except getopt.GetoptError as err:
        # print help information and exit:
        usage()
        logging.error (err) # will print something like "option -a not recognized"
        sys.exit(2)
    
    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        elif o in ("-e", "--createEmptyDB"):
            createEmptyDB= True
        elif o in ("-i", "--inputDirectory"):
            inputDirectory = a
            if inputDirectory == "":
                print ("- Nmap directory not specified")
                sys.exit(2)
            else:
                print ("+ Nmap directory specified as: " + inputDirectory)
                inputSpecified = True
        elif o in ("-o", "--outputDB"):
            outputDB = a
            if outputDB == "":
                print ("- Database filename not specified")
                sys.exit(2)
            else:
                print ("+ Database specified as: " + outputDB)
                dbOutputSpecified = True
        elif o in ("-a", "--appendDatabase"):
            createDatabase = False
        elif o in ("-s", "--filenameStarts"):
            filenameStartsWith = a
            if filenameStartsWith == "":
                logging.error ("- Filename 'starts with' option selected, but no string has been specified - will traverse all nmap xml files found")
        elif o in ("-q", "--query"):
            query = a
            querySpecified = True
        elif o in ("-O", "--logOutputFilname"):
            logName = a
        elif o in ("-l", "--loggingLevel"):
            loggingLevel = a 
   
    # need some validation here for the output log name being set etc - ISNT WORKING YET!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    logging_level=LOGGING_LEVELS.get(loggingLevel, logging.NOTSET)
    logging.basicConfig(level=LOGGING_LEVELS.get('debug', logging.NOTSET), filename=logName, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    console = logging.StreamHandler()
    console.setLevel(logging_level)
    formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
 
    logging.info("=====================================================================")
    logging.info("| NmapResultParser v0.2 - A Hopkins")
    logging.info("=====================================================================")
    
    # instantiate the directory parser
    dp = directoryParser.DirectoryParser(inputDirectory, outputDB)
    ndb = nmapDatabase.nmapDatabase(outputDB)
    
    # create an empty database then exit
    if createEmptyDB == True:
        if dbOutputSpecified == True and os.path.exists(outputDB) == False:
            ndb.createDatabase()
            sys.exit(2)
        elif dbOutputSpecified == True and os.path.exists(outputDB) == True:
            logging.error ("- The specified output database already exists")
            sys.exit(2)
        else:
            logging.error ("- The create empty database option has been selected, but no output location has been specified")
            sys.exit(2)

    # create or append to an existing database using NMAP data contained within the input directory
    if inputSpecified == True and dbOutputSpecified == True:   
        # Create a database if one doesn't already exist
        if createDatabase == True and os.path.exists(outputDB) == False:   
            ndb.createDatabase()
        elif createDatabase == False and os.path.exists(outputDB) == True:
            logging.info ("+ Appending results to the pre-existing database at " + outputDB)
        elif createDatabase == False and os.path.exists(outputDB) == False:
            logging.error ("- Cannot find the specified database to append nmap data, remove the -a option to create a new database")  
            sys.exit(2)      
        else:
            logging.error ("- Database already exists, use -a option to append results to existing database, otherwise choose another output filename")
            sys.exit(2)
        # traverse directory for potential nmap xml files. Add results to specified database.
        dp.traverseDirectory(filenameStartsWith)
 
    # else user just wants to query the database
    elif dbOutputSpecified == True and querySpecified == True:
        logging.info ("+ Querying database outputDB")
    # else the user hasn't supplied the required information
    elif querySpecified == True and dbOutputSpecified == False:
        logging.error ("- Database to query has not been specified")
        sys.exit(2)
    else:
        logging.error ("- Nmap input Directory or database file not specified")
        sys.exit(2)
    
    if dbOutputSpecified == True and querySpecified == True:
        logging.info ("+ Querying database")
        logging.debug (query)
        ndb.query(query)    
    
    logging.info ("+ Finished Parsing");
    
if __name__ == '__main__':
    main()
    
    
